<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>map-travel</title>
    <style>
      html body {
        height: 100%;
        width: 100%;
        margin: 0;
        padding: 0;
        overflow: hidden;
      }
    </style>
  </head>
  <body>
    <script src="./js/three.js"></script>
    <script src="./js/d3.js"></script>
    <script>
      const scene = new THREE.Scene();

      const renderer = new THREE.WebGLRenderer();
      renderer.setClearColor(0x000000);
      renderer.setSize(window.innerWidth, window.innerHeight);
      document.body.appendChild(renderer.domElement);

      const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
      camera.position.set(0, 0, 10);
      camera.lookAt(scene.position);

      let ambientLight = new THREE.AmbientLight(0xffffff);
      scene.add(ambientLight);

      function create() {
          const loader = new THREE.FileLoader();
          loader.load('./data/china.json', (data) => {
            const jsondata = JSON.parse(data);
            generateGeometry(jsondata);
          })
      }

      const projection = d3.geoMercator()
            .center([116.412318,39.909843])
            .translate([0, 0]);

      let beijingPosition= projection([116.412318,39.909843]);
      let shanghaiPosition = projection([121.495721,31.236797]);

      function drawBoundary(polygon) {
        const lineGeometry = new THREE.Geometry();

        for (let i = 0; i < polygon.length; i++) {
          const [x, y] = projection(polygon[i]);
          lineGeometry.vertices.push(new THREE.Vector3(x, -y, 0));
        }

        const lineMaterial = new THREE.LineBasicMaterial({ 
          color: 'yellow'
        });

        return new THREE.Line(lineGeometry, lineMaterial);
      }

      function drawExtrudeMesh(polygon, color) {
        const shape = new THREE.Shape();

        for (let i = 0; i < polygon.length; i++) {
          const [x, y] = projection(polygon[i]);

          if (i === 0) {
            shape.moveTo(x, -y);
          }

          shape.lineTo(x, -y);
        }

        const geometry = new THREE.ExtrudeGeometry(shape, {
          depth: 0,
          bevelEnabled: false
        });

        const material = new THREE.MeshBasicMaterial({
          color,
          transparent: true,
          opacity: 0.2,
        })
        
        return new THREE.Mesh(geometry, material);
      }

      function generateGeometry(jsondata) {
          const map = new THREE.Group();

          jsondata.features.forEach((elem) => {
            const province = new THREE.Group();

            const coordinates = elem.geometry.coordinates;
            coordinates.forEach((multiPolygon) => {
              multiPolygon.forEach((polygon) => {
                const line = drawBoundary(polygon);

                const provinceColor = ['北京市', '上海市'].includes(elem.properties.name) ? 'yellow' : 'blue';
                const mesh = drawExtrudeMesh(polygon, provinceColor);
                
                province.add(line);
                province.add(mesh);
              });
            });

            map.add(province);
          })

          scene.add(map);
          const line = drawLine(beijingPosition, shanghaiPosition);
          scene.add(line);

      }

      function render() {
        if(camera.position.x < shanghaiPosition[0]) {
            camera.position.x += 0.1;
        }  
        if(camera.position.y > -shanghaiPosition[1]) {
            camera.position.y -= 0.2;
        }
        renderer.render(scene, camera);
        requestAnimationFrame(render);
      }

      function drawLine(pos1, pos2) {
          const [x0, y0, z0] = [...pos1, 0];
          const [x1, y1, z1] = [...pos2, 0];

          const geomentry = new THREE.Geometry();
          geomentry.vertices = new THREE.QuadraticBezierCurve3(
              new THREE.Vector3(-x0, -y0, z0),
              new THREE.Vector3(-(x0 + x1) / 2, -(y0 + y1) / 2, -10),
              new THREE.Vector3(-x1, -y1, z1),
          ).getPoints();

          const material = new THREE.LineBasicMaterial({color: 'white'});

          const line = new THREE.Line(geomentry, material);
          line.rotation.y = Math.PI;

          return line;
      }

      create();
      render();
    </script>
  </body>
</html>
